#include "../process.h"
#include "../util.h"
#include "../config.h"
#include "../log/log_mgr.h"

#define BATCH 20000

vector<int> mark_list;
map<string, int> query2index;
map<int, vector<string>>index2query;
map<string, int> query2freq;
int union_limit, size_limit, deep_limit;

inline pair<int, int> find_idx(string query)
{
	int idx = (*query2index.find(query)).second, itr = idx, depth = 0;
	while(mark_list[idx] > 0){
		depth ++;
		idx = mark_list[idx];
	}
	return pair<int, int>(idx, depth);
}

inline void union_query(string q1, string q2)
{
	pair<int, int> pair1 = find_idx(q1), pair2 = find_idx(q2);
	int idx1 = pair1.first, depth1 = pair1.second, idx2 = pair2.first, depth2 = pair2.second;
	if(idx1 == idx2)return;
	if(- mark_list[idx1] - mark_list[idx2] < size_limit)
	{
		if(mark_list[idx1] > mark_list[idx2])
		{
			if(depth1 < deep_limit && -mark_list[idx2] < union_limit)
			{
				mark_list[idx2] += mark_list[idx1]; 
				mark_list[idx1] = idx2;
			}
		}
		else{
			if(depth2 < deep_limit && -mark_list[idx1] < union_limit)
			{
				mark_list[idx1] += mark_list[idx2]; 
				mark_list[idx2] = idx1;
			}
		}
	}
}

inline void aggregate()
{
	map<string, int>::iterator itr;
	for(itr = query2index.begin(); itr != query2index.end(); ++ itr)
	{
		string query = (*itr).first;
		int idx = find_idx(query).first;
		map<int, vector<string>>::iterator inver_itr = index2query.find(idx);
		if(inver_itr == index2query.end())
		{
			vector<string> v;
			v.push_back(query);
			index2query.insert(pair<int, vector<string>>(idx, v));
		}
		else
			(*inver_itr).second.push_back(query);
	}
}

inline void display(iostream* out)
{
	stringstream res;
	map<int, vector<string>>::iterator itr;
	int i = 0;
	for(itr = index2query.begin(); itr != index2query.end() ; ++ itr)
	{
		vector<string> &cluster = (*itr).second;
		stringstream group_info;
		group_info << i ++ << "\t";
		int total = 0;
		map<int, vector<string>> sort_map;
		map<int, vector<string>>::iterator itr;
		int total_q = 0, cur_q = 0;
		for(int j = 0; j < cluster.size(); ++ j)
		{
			string query = cluster[j];
			total_q ++;
			int freq = (*query2freq.find(query)).second;
			itr = sort_map.find(-freq);
			if(itr == sort_map.end())
			{
				vector<string> v;
				v.push_back(query);
				sort_map.insert(pair<int, vector<string>>(-freq, v));
			}
			else
				(*itr).second.push_back(query);
			total += freq;
			
		}
		for(itr = sort_map.begin(); itr != sort_map.end(); ++ itr)
		{
			int freq = - (*itr).first;
			vector<string> &query_list = (*itr).second;
			for(int j = 0; j < query_list.size(); ++ j)
			{
				string query = query_list[j];
				group_info << query << "\t" << freq;
				if(++ cur_q < total_q)
					group_info << " | ";
			}
		}
		res << total_q << "\t" << group_info.str() << endl;
	}
	*out << res.str();
}


int main(int argc, const char* argv[])
{
	fstream cluster_file(argv[1], fstream::in);
	fstream session_file(argv[2], fstream::in);
	fstream query2freq_file(argv[3], fstream::in);
	fstream output(argv[4], fstream::out);
	fstream conf_file(argv[5], fstream::in);
	Config::load(&conf_file);

	union_limit = atoi(Config::get("Union_limit").c_str());
	size_limit = atoi(Config::get("Size_limit").c_str());
	deep_limit = atoi(Config::get("Deep_limit").c_str());

	int count_q2f = 0, count_cf = 0, count_sf = 0;
	util::LogMgr::init(argc, argv);
	util::LogMgr::time_start();

	cout << "reading query2freq_file\n";
	while(!query2freq_file.eof())
	{
		string line;
		getline(query2freq_file, line);
		if(line.length() == 0)continue;
		if(count_q2f++ %BATCH == 0)cout << ".";
		int del1 = line.find("\t"), del2 = line.find("\t", del1 + 1);
		int freq = atoi(line.substr(0, del1).c_str());
		string query = line.substr(del1 + 1, del2 - del1 - 1);
		query2freq.insert(pair<string, int>(query, freq));
	}
	cout << "\nreading clustring_file\n";
	while(!cluster_file.eof())
	{
		string line;
		getline(cluster_file, line);
		if(line.length() == 0)continue;
		if(count_cf++ %BATCH == 0)cout << ".";
		int del1 = line.find("\t"), del2 = line.find("\t", del1 + 1);
		string elem_s = line.substr(del2 + 1, line.length() - del2 - 1);
		vector<string> elem = util::tokenize_h(elem_s, " | ");
		int cur_cluster = mark_list.size();
		for(int i = 0; i < elem.size(); ++ i)
		{
			int del = elem[i].find("\t");
			string query = elem[i].substr(0, del);
			query2index.insert(pair<string, int>(query, mark_list.size()));
			if(i == 0)
				mark_list.push_back(-elem.size());
			else
				mark_list.push_back(cur_cluster);			
		}
	}
	cout << "\nreading session_file\n";
	while(!session_file.eof())
	{
		string line;
		getline(session_file, line);
		if(line.length() == 0)continue;
		if(count_sf++ %BATCH == 0)cout << ".";
		int del = line.find(":");
		string query = line.substr(0, del);
		string elem_s = line.substr(del + 1, line.length() - del - 1);
		map<string, int>::iterator itr = query2index.find(query);
		if(itr == query2index.end())
		{
			query2index.insert(pair<string, int>(query, mark_list.size()));
			mark_list.push_back(-1);
		}
		vector<string> elem = util::tokenize_h(elem_s, " | ");
		for(int i = 0; i < elem.size(); ++ i)
		{
			map<string, int>::iterator elem_itr = query2index.find(elem[i]);
			if(elem_itr == query2index.end())
			{
				query2index.insert(pair<string, int>(elem[i], mark_list.size()));
				mark_list.push_back(-1);
			}
			union_query(query, elem[i]);
		}
	}
	cout << "\naggregating\n";
	aggregate();
	display(&output);
	cout << endl;
	util::LogMgr::time_end();

	cluster_file.close();
	session_file.close();
	output.close();
	query2freq_file.close();
	conf_file.close();
	return 0;
}